/*
 * susi.cpp
 * susi is a Arduino communication library for communication with SUSI decoder modules
 * SUSI is a Trademark of DIETZ Modellbahntechnik www.d-i-e-t-z.de
 * Licensed for use under the terms of the GNU General Public License v3
 * V0.1 	MM 20.02.2013
 * 20.02.2013, Mark Martin, Munich, Germany
 * URC Project
 */

#include "susi.h"

#define CLKTime 10 								//Set Susi clock periode time to 10µs

susi::susi( unsigned char DT, unsigned char CK )
{	
	ckPin = CK;
	dtPin = DT;
}

susi::~susi() {
	end();
}

void susi::begin() {
  pinMode ( ckPin, OUTPUT);				// Set clock as output
  pinMode ( dtPin, INPUT);			  // Set data as high-Z
  digitalWrite( ckPin, LOW );			// Set clock to 0
  digitalWrite( dtPin, LOW );			// Set data to 0
  
  susiActiveFlag = false;
  susiActiveCounter = 0;
  susiTxBufferCounter = 0;

}

void susi::end() {
	pinMode ( ckPin, INPUT);        // high-Z pins on destruction
  pinMode ( dtPin, INPUT);
}

void susi::everyloop( void ) {
  static long int Micros = 0;
  
  if( susiActiveFlag ) {
    if(( micros() - Micros ) > CLKTime ) {      //Time for update?
      if( susiActiveCounter & 0x01 ) {
        digitalWrite( ckPin, LOW );             //Set clock low
      } else {
        digitalWrite( ckPin, HIGH );            //Set clock high
        digitalWrite( dtPin, ( susiTxBuffer[ 0 ] >> ( susiActiveCounter >> 1 )) & 0x01 );
                                                //Write the bits through the DT pin
      }
      susiActiveCounter++;
      if( susiActiveCounter >= 16 ) {           //Byte transmitted?
        susiActiveCounter = 0;                  //Reset counter
        susiTxBufferCounter--;
       if( susiTxBufferCounter == 0 ) {
         susiActiveFlag = false;                //Reset flag
         pinMode (dtPin, INPUT);
       } else {
          susiTxBuffer[ 0 ] = susiTxBuffer[ 1 ];
          susiTxBuffer[ 1 ] = susiTxBuffer[ 2 ];
        }
      }
      Micros = micros();                        //Backup system counter
    }
  }
}

inline boolean susi::active( void ) {
	return( susiActiveFlag );
}

boolean susi::sendCommand( byte adress, byte value )  {
  boolean returnvalue;
  // Valid adresses:
  // 0x60    F0F4
  // 0x61    F5F12
  // 0x24    VLOCO
  // 0x25    VCPU
  // 0x26    LLOCO
  // 0x00    NOP
  if( susiActiveFlag ) {
    returnvalue = false;
  } else {
    susiTxBuffer[ 0 ] = adress;
    susiTxBuffer[ 1 ] =  value;
    susiTxBufferCounter = 2;
    susiActiveFlag = true;
    susiActiveCounter = 0;
    returnvalue = true;
    pinMode (dtPin, OUTPUT);
  }
  return( returnvalue );
}

boolean susi::sendF0F4( byte fkey ) {
	return( sendCommand( 0x60, (( fkey & 0x1E ) >> 1 ) | (( fkey & 0x01 ) << 4 ) ));
	//0 0 0 F4 F3 F2 F1 F0 -> 0 0 0 F0 F4 F3 F2 F1
}

boolean susi::sendF5F12( byte fkey ) {
	return( sendCommand( 0x61, fkey ));
	//F12 F11 F10 F9 F8 F7 F6 F5
}

boolean susi::sendVLOCO( byte vloco, boolean dir ) {
	return( sendCommand( 0x24, ( vloco & 0x7f ) | dir ));
	//real speed
}

boolean susi::sendVCPU( byte vcpu, boolean dir ) {
	return( sendCommand( 0x24, ( vcpu & 0x7f ) | dir ));
	//requested speed
}

boolean susi::sendLLOCO( byte lloco, boolean dir ) {
	return( sendCommand( 0x24, lloco & 0x7f ));
	//power
}

boolean susi::sendCV( unsigned int adress, byte value )  {
  boolean returnvalue;
  // Valid CV adresses: 897 to 1024
  if( susiActiveFlag ) {
    returnvalue = false;
  } else {
	  if( adress < 897 ) {
		  returnvalue = false;
	  } else {
	    susiTxBuffer[ 0 ] = ( byte )(( adress & 0x0300 ) >> 8 ) | 0x7C;
	    susiTxBuffer[ 1 ] = ( byte )adress & 0x00ff;
	    susiTxBuffer[ 2 ] =  value;
	    susiTxBufferCounter = 3;
	    susiActiveFlag = true;
	    susiActiveCounter = 0;
	    returnvalue = true;
	    pinMode (dtPin, OUTPUT);
	  }
  }
  return( returnvalue );
  }

